/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.billy.ohos.swipe.ext.refresh;

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//


import com.billy.ohos.swipe.PointF;
import ohos.agp.components.Component;
import ohos.agp.render.*;
import ohos.agp.utils.Color;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.miscservices.timeutility.Time;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ArrowDrawable extends Component {
    public static final int STATE_NORMAL = 0;
    public static final int STATE_DRAGGING = 1;
    public static final int STATE_FIRING = 3;
    public static final int STATE_HITTING = 4;
    public static final int STATE_MISSING = 5;
    private float mPrecision = 2.0F;
    private int mState;
    private float mProgress;
    private int mWidth;
    private int mHeight;
    private float mCenterX;
    private float mBowLength;
    private float mBowWidth;
    private float mStringWidth;
    private float mHandleWidth;
    private float mArrowBodyLength;
    private float mArrowBodyWidth;
    private float mFinHeight;
    private float mFinWidth;
    private float mFinSlopeHeight;
    private float mArrowWidth;
    private float mArrowHeight;
    private Path mBowPath = new Path();
    private float[] mBowPathPoints;
    private Path mHandlePath = new Path();
    private Path mArrowPath = new Path();
    private PointF mStringStartPoint = new PointF();
    private PointF mStringMiddlePoint = new PointF();
    private PointF mStringEndPoint = new PointF();
    private RectF mArrowTail = new RectF();
    private List<ArrowDrawable.Line> mLines;
    private float mMaxBowOffset;
    private float mBaseStringCenterY;
    private float mMaxStringOffset;
    private float mStringOffset;
    private float mArrowOffset;
//    private final float mBaseAngle = 25.0F;
//    private final float mUsableAngle = 20.0F;
    private int mBaseLinesFallDuration = 200;
    private long mFiringBowFallDuration = 100L;
    private long mFireTime;
    private float mFiringBowOffsetDistance;
    private long mFiredArrowShrinkDuration = 200L;
    private long mFiredArrowShrinkStartTime;
    private float mFiredArrowShrinkDistance;
    private long mFiredArrowMoveDuration = 200L;
    private long mFiredArrowMoveStartTime;
    private float mFiredArrowMoveDistance;
    private float mFiredArrowLastMoveDistance;
    private long mMissDuration = 400L;
    private long mMissStartTime;
    private float mMissDistance;
    private long mHitDuration = 50L;
    private long mHitStartTime;
    private float mHitDistance;
    private float mSkewDuration = 25.0F;
    private float mSkewStartTime;
    private float mSkewTan = 0.035F;
    private int mMaxSkewCount = 9;
    private int mCurrentSkewCount;
    private int mLineColor = -1;
    private int mBowColor = -1;
    private int mStringColor = -1;
    private int mArrowColor = -1;
    private Paint mPaint;
    private PathMeasure mBowPathMeasure;
    private ScaleHelper mScaleHelper;
    private MaskFilter mTailMaskFilter;
    private SecureRandom mRandom = new SecureRandom();
    private PointF mTempPoint = new PointF();

    static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201, "my_tag");

    /***
     * 创建 ArrowDrawable 对象
     * @param targetView 目标view
     * @return 返回 ArrowDrawable 的对象
     */
    public static ArrowDrawable create(final Component targetView) {
        //targetView.setLayerType(1, (Paint)null);
        int bowLength = (int)((float)targetView.getWidth() * 0.4F);
        final ArrowDrawable drawable = new ArrowDrawable(targetView.getContext(),targetView.getWidth(), targetView.getHeight(), bowLength);
        if (targetView.getWidth() == 0 || targetView.getHeight() == 0) {
            targetView.setLayoutRefreshedListener(new Component.LayoutRefreshedListener(){

                @Override
                public void onRefreshed(Component component) {
                    if (targetView.getWidth() > 0 || targetView.getHeight() > 0) {
                        int bowLength = (int)((float)targetView.getWidth() * 0.4F);
                        drawable.updateSize(targetView.getWidth(), targetView.getHeight(), bowLength);
                        //targetView.getComponentTreeObserver().removeTreeLayoutChangedListener();
                    }
                }
            });
        }

        return drawable;
    }

    /***
     * 创建 ArrowDrawable 对象
     * @param targetView 目标view
     * @param width 指定宽
     * @param height 指定高
     * @return 返回 ArrowDrawable 的对象
     */
    public static ArrowDrawable create(Component targetView, int width, int height) {
        return create(targetView, width, height, (int)((float)width * 0.4F));
    }

    /***
     * 创建 ArrowDrawable 对象
     * @param targetView 目标view
     * @param width 指定宽
     * @param height 指定高
     * @param bowLength 弓长
     * @return 返回 ArrowDrawable 的对象
     * @throws IllegalArgumentException IllegalArgumentException
     */
    public static ArrowDrawable create(Component targetView, int width, int height, int bowLength) {
        //targetView.setLayerType(1, (Paint)null);
        if (width > 0 && height > 0) {
            return new ArrowDrawable(targetView.getContext(),width, height, bowLength);
        } else {
            throw new IllegalArgumentException("Invalid size!");
        }
    }

    private ArrowDrawable(Context context, int width, int height, int bowLength) {
        super(context);
        this.initPaint();
        this.mScaleHelper = new ScaleHelper(new float[]{0.2F, 0.0F, 1.0F, 0.05F, 2.0F, 0.5F, 1.0F, 0.95F, 0.2F, 1.0F});
        this.updateSize(width, height, bowLength);
        this.initLines();
    }

    /***
     * 重置view
     */
    public void reset() {
        this.resetWithoutInvalidate();
        this.invalidate();
    }

    private void resetWithoutInvalidate() {
        this.mState = 0;
        this.mProgress = 0.0F;
        this.mArrowOffset = 0.0F;
        this.mStringOffset = 0.0F;
        this.initArrowPath(this.mArrowBodyLength);
    }

    /***
     * hit 击
     */
    public void hit() {
        if (this.mState == 3 && this.mFiredArrowMoveStartTime > 0L) {
            this.mState = 4;
            this.mHitStartTime = Time.getRealActiveTime();
            float currentArrowOffset = this.mArrowOffset + this.mFiredArrowLastMoveDistance;
            if (this.mFiredArrowMoveDistance > 0.0F) {
                currentArrowOffset -= this.mFiredArrowMoveDistance;
            }

            float arrowBodyHeight = this.mFinHeight + this.mFinSlopeHeight + this.mArrowBodyLength;
            this.mHitDistance = -(currentArrowOffset - arrowBodyHeight);
            this.mFiredArrowLastMoveDistance = 0.0F;
            this.invalidate();
        }

    }

    /***
     * miss 错过
     */
    public void miss() {
        if (this.mState == 3 && this.mFiredArrowMoveStartTime > 0L) {
            this.mState = 5;
            this.mMissStartTime = Time.getRealActiveTime();
            float currentArrowOffset = this.mArrowOffset + this.mFiredArrowLastMoveDistance;
            if (this.mFiredArrowMoveDistance > 0.0F) {
                currentArrowOffset -= this.mFiredArrowMoveDistance;
            }

            this.mMissDistance = -(currentArrowOffset + mArrowTail.height());
            this.mFiredArrowLastMoveDistance = 0.0F;
            this.invalidate();
        }

    }

    /***
     * fire 发射
     */
    public void fire() {
        if (this.mProgress >= 0.95F && this.mState == 1) {
            this.mState = 3;
            Iterator var1 = this.mLines.iterator();

            while(var1.hasNext()) {
                ArrowDrawable.Line tmp = (ArrowDrawable.Line)var1.next();
                this.initLines(tmp);
            }

            this.mFiredArrowShrinkStartTime = 0L;
            this.mFiredArrowMoveStartTime = 0L;
            this.mFiredArrowLastMoveDistance = 0.0F;
            this.mFiredArrowMoveDistance = -Math.abs(this.mFiredArrowMoveDistance);
            this.mFireTime = Time.getRealActiveTime();
            this.invalidate();
        }

    }

    private void initLines() {
        this.mLines = new ArrayList(6);

        for(int i = 0; i < 6; ++i) {
            this.mLines.add(new ArrowDrawable.Line());
        }

    }

    private void initLines(ArrowDrawable.Line tmp) {
        tmp.startTime = Time.getRealActiveTime();
        tmp.duration = (long)(this.mBaseLinesFallDuration / 4 + this.mRandom.nextInt(this.mBaseLinesFallDuration));
        tmp.startY = (float)(-this.mHeight) + this.mRandom.nextFloat() * (float)this.mHeight;
        tmp.height = -tmp.startY;
        tmp.startX = this.mRandom.nextFloat() * (float)this.mWidth;
        tmp.endX = tmp.startX;
        tmp.distance = (float)this.mHeight - tmp.startY;
    }

    /***
     * 更新大小
     * @param width 宽度
     * @param height 高度
     * @param bowLength 弓长
     */
    public void updateSize(int width, int height, int bowLength) {
        this.mWidth = width;
        this.mHeight = height;
        this.mBowLength = (float)bowLength;
        this.mCenterX = (float)this.mWidth / 2.0F;
        this.mStringMiddlePoint.x = this.mCenterX;
        this.mBowWidth = this.mBowLength / 50.0F;
        this.mStringWidth = this.mBowWidth / 3.0F;
        this.mHandleWidth = this.mBowWidth * 2.5F;
        this.mArrowBodyLength = this.mBowLength / 2.0F;
        this.mArrowBodyWidth = this.mArrowBodyLength / 70.0F;
        this.mFinHeight = this.mArrowBodyLength / 6.0F;
        this.mFinWidth = this.mFinHeight / 3.0F;
        this.mFinSlopeHeight = this.mFinWidth;
        this.mArrowWidth = this.mFinWidth;
        this.mArrowHeight = this.mArrowBodyLength / 8.0F;
        this.mFiredArrowShrinkDistance = this.mArrowBodyLength * 0.3F;
        this.mFiredArrowMoveDistance = this.mFinHeight;
        this.mBaseStringCenterY = this.getPointByAngle(25.0F).y + this.mBowWidth;
        float bowHeight = this.mBaseStringCenterY + this.mHandleWidth / 2.0F;
        this.mMaxBowOffset = bowHeight + ((float)this.mHeight - this.mArrowBodyLength) / 2.0F;
        this.mMaxStringOffset = this.mArrowBodyLength - bowHeight;
        this.mFiringBowOffsetDistance = (float)this.mHeight - this.mMaxBowOffset + bowHeight;
        if (this.mFinWidth > 0.0F) {
            this.mTailMaskFilter = new MaskFilter(this.mFinWidth, MaskFilter.Blur.NORMAL);
        }

        this.mPaint.setPathEffect(new PathEffect(this.mBowWidth));
        this.initArrowPath(this.mArrowBodyLength);
        this.initArrowTail();
        this.invalidate();
    }

    private void initPaint() {
        this.mPaint = new Paint();
        this.mPaint.setAntiAlias(true);
        this.mPaint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);
        this.mPaint.setStrokeJoin(Paint.Join.ROUND_JOIN);
    }

    private void initArrowTail() {
        float tailHeight = this.mFinHeight * 2.0F;
        this.mArrowTail.set((this.mCenterX - this.mFinWidth), 0.0F, (this.mCenterX + this.mFinWidth), tailHeight);
    }

    /***
     * 画
     * @param canvas 画布
     */
    public void draw( Canvas canvas) {
        switch(this.mState) {
            case 3:
                this.handleFiringState(canvas);
                break;
            case 4:
                this.handleHittingState(canvas);
                break;
            case 5:
                this.handleMissingState(canvas);
                break;
            default:
                this.updateBowPath(this.getAngleByProgress());
                this.drawBowPath(canvas);
                this.updateHandlePath();
                this.drawHandlePath(canvas);
                this.updateStringPoints();
                this.drawString(canvas);
                this.updateArrowOffset();
                this.drawArrow(canvas);
        }

    }

    private void handleFiringState(Canvas canvas) {
        float firedTime = (float)(Time.getRealActiveTime() - this.mFireTime);
        if (firedTime <= (float)this.mFiringBowFallDuration) {
            this.drawBowFalling(canvas, firedTime);
        }

        this.drawLinesAndArrow(canvas);
        this.invalidate();
    }

    private void handleHittingState(Canvas canvas) {
        if (this.mHitStartTime > 0L) {
            this.drawArrowHitting(canvas);
            this.invalidate();
        } else if (this.mSkewStartTime > 0.0F) {
            this.drawArrowSkewing(canvas);
        } else {
            this.drawArrow(canvas);
        }

    }

    private void handleMissingState(Canvas canvas) {
        if (this.mMissStartTime > 0L) {
            float runTime = (float)(Time.getRealActiveTime() - this.mMissStartTime);
            float percent = runTime / (float)this.mMissDuration;
            if (percent > 1.0F) {
                percent = 1.0F;
            }

            float distance = percent * this.mMissDistance;
            float offset = distance - this.mFiredArrowLastMoveDistance;
            this.mFiredArrowLastMoveDistance = distance;
            this.mArrowTail.offset(0.0F, offset);
            this.mArrowPath.offset(0.0F, offset);
            if (percent < 1.0F) {
                this.drawLines(canvas);
                this.updateLinesY();
            }

            this.drawArrow(canvas);
            this.drawArrowTail(canvas);
            if (percent == 1.0F) {
                this.mMissStartTime = 0L;
                return;
            }
        }

        this.invalidate();
    }

    private void drawBowFalling(Canvas canvas, float firedTime) {
        float percent = firedTime / (float)this.mFiringBowFallDuration;
        if (percent > 1.0F) {
            percent = 1.0F;
        }

        float angle = this.getAngleByProgress() - percent * 3.0F * 20.0F;
        if (angle < 25.0F) {
            angle = 25.0F;
        }


        this.updateBowPath(angle);
        this.mBowPath.offset(0.0F, percent * this.mFiringBowOffsetDistance);
        this.drawBowPath(canvas);
        this.updateHandlePath();
        this.drawHandlePath(canvas);
        this.updateStringPoints(false);

        // 原库的bug，滑动过慢时，此判断可能进不去，mStringMiddlePoint.y 可能比 mStringStartPoint.y 大
        //if (this.mStringMiddlePoint.y < this.mStringStartPoint.y) {
            this.mStringMiddlePoint.y = this.mStringStartPoint.y;
            if (this.mFiredArrowShrinkStartTime == 0L) {
                this.mFiredArrowShrinkStartTime = Time.getRealActiveTime();
            }
        //}

        this.drawString(canvas);
        this.drawArrow(canvas);
    }

    private void drawArrowSkewing(Canvas canvas) {
        float runTime = (float)Time.getRealActiveTime() - this.mSkewStartTime;
        float percent = runTime / this.mSkewDuration;
        if (percent > 1.0F) {
            percent = 1.0F;
        }

        float tan = this.mSkewTan * percent;
        if (this.mCurrentSkewCount % 2 == 0) {
            tan -= this.mSkewTan;
        }

        canvas.skew(tan, 0.0F);
        this.drawArrow(canvas);
        if (percent == 1.0F) {
            if (this.mCurrentSkewCount == this.mMaxSkewCount) {
                this.mSkewStartTime = 0.0F;
                return;
            }
            this.mSkewStartTime = (float)Time.getRealActiveTime();
            ++this.mCurrentSkewCount;
            if (this.mCurrentSkewCount % 2 == 0) {
                this.mSkewTan = -this.mSkewTan;
            }
        }

        this.invalidate();
    }

    private void drawArrowHitting( Canvas canvas) {
        float runTime = (float)(Time.getRealActiveTime() - this.mHitStartTime);
        float percent = runTime / (float)this.mHitDuration;
        if (percent > 1.0F) {
            percent = 1.0F;
            this.mHitStartTime = 0L;
            this.mSkewStartTime = (float)Time.getRealActiveTime();
            this.mCurrentSkewCount = 1;
        }

        float distance = percent * this.mHitDistance;
        float offset = distance - this.mFiredArrowLastMoveDistance;
        this.mFiredArrowLastMoveDistance = distance;
        this.mArrowTail.offset(0.0F, offset);
        this.mArrowPath.offset(0.0F, offset);
        this.drawLines(canvas);
        this.updateLinesY();
        this.drawArrow(canvas);
        this.drawArrowTail(canvas, (int)(255.0F * (1.0F - percent)));
        this.mPaint.setAlpha(toFloat(255));
    }

    private float toFloat(int alpha){
        return alpha / 255.F;
    }

    private void drawLinesAndArrow(Canvas canvas) {
        if (this.mFiredArrowMoveStartTime > 0L) {
            this.drawLines(canvas);
            this.updateLinesY();
            this.drawDancingArrow(canvas);
        } else if (this.mFiredArrowShrinkStartTime > 0L) {
            this.drawShrinkingArrow(canvas);
        }

    }



    private void drawDancingArrow(Canvas canvas) {
        float runTime = (float)(Time.getRealActiveTime() - this.mFiredArrowMoveStartTime);
        float percent = runTime / (float)this.mFiredArrowMoveDuration;
        if (percent > 1.0F) {
            percent = 1.0F;
        }

        float distance = percent * this.mFiredArrowMoveDistance;
        float offset = distance - this.mFiredArrowLastMoveDistance;
        this.mFiredArrowLastMoveDistance = distance;
        this.mArrowTail.offset(0.0F, offset);
        this.mArrowPath.offset(0.0F, offset);
        this.drawArrow(canvas);
        this.drawArrowTail(canvas);
        if (percent == 1.0F) {
            this.mFiredArrowMoveStartTime = Time.getRealActiveTime();
            this.mFiredArrowMoveDistance = -this.mFiredArrowMoveDistance;
            this.mFiredArrowLastMoveDistance = 0.0F;
        }

    }

    private void drawShrinkingArrow(Canvas canvas) {
        float runTime = (float)(Time.getRealActiveTime() - this.mFiredArrowShrinkStartTime);
        float percent = runTime / (float)this.mFiredArrowShrinkDuration;
        if (percent > 1.0F) {
            percent = 1.0F;
        }
        float needSubtractLength = percent * this.mFiredArrowShrinkDistance;
        float arrowLength = this.mArrowBodyLength - needSubtractLength;
        this.initArrowPath(arrowLength);
        float newArrowOffset = this.mArrowOffset - needSubtractLength;
        this.mArrowPath.offset(0.0F, newArrowOffset);
        this.mArrowTail.offsetTo(this.mArrowTail.left, newArrowOffset - this.mFinHeight / 2.0F);
        this.drawArrowTail(canvas, (int)(255.0F * percent));
        this.mPaint.setAlpha(toFloat(255));
        this.drawArrow(canvas);
        if (percent == 1.0F) {
            this.mFiredArrowShrinkStartTime = 0L;
            this.mFiredArrowMoveStartTime = Time.getRealActiveTime();
        }

    }

    private void offsetTo(Rect rect, float newLeft, float newTop) {
        rect.right += newLeft - rect.left;
        rect.bottom += newTop - rect.top;
        rect.left = (int)newLeft;
        rect.top = (int)newTop;
    }

    private void drawLines(Canvas canvas) {
        this.mPaint.setStyle(Paint.Style.STROKE_STYLE);
        this.mPaint.setStrokeWidth(this.mArrowBodyWidth);
        this.mPaint.setColor(new Color(this.mLineColor) );
        Iterator var2 = this.mLines.iterator();
        while(var2.hasNext()) {
            ArrowDrawable.Line tmp = (ArrowDrawable.Line)var2.next();
            canvas.drawLine(tmp.startX, tmp.startY, tmp.endX, tmp.startY + tmp.height, this.mPaint);
        }

    }

    private void drawBowPath(Canvas canvas) {
        this.mBowPathMeasure = new PathMeasure(this.mBowPath, false);
        this.mBowPathPoints = this.decomposePath(this.mBowPathMeasure);
        this.mPaint.setStyle(Paint.Style.FILL_STYLE); 
        this.mPaint.setColor(new Color(this.mBowColor));
        int length = this.mBowPathPoints.length;
        for(int i = 0; i < length; i += 2) {
            float fraction = (float)i / (float)length;
            float radius = this.mBowWidth * this.mScaleHelper.getScale(fraction) / 2.0F;
            canvas.drawCircle(this.mBowPathPoints[i], this.mBowPathPoints[i + 1], radius, this.mPaint);
        }

    }

    private void drawHandlePath(Canvas canvas) {
        this.mPaint.setStyle(Paint.Style.STROKE_STYLE);
        this.mPaint.setStrokeWidth(this.mHandleWidth);
        this.mPaint.setColor(new Color(this.mBowColor));
        canvas.drawPath(this.mHandlePath, this.mPaint);
    }

    private void drawString(Canvas canvas) {
        this.mPaint.setStyle(Paint.Style.STROKE_STYLE);
        this.mPaint.setStrokeWidth(this.mStringWidth);
        this.mPaint.setColor(new Color(this.mStringColor));
        canvas.drawLine(this.mStringStartPoint.x, this.mStringStartPoint.y, this.mStringMiddlePoint.x, this.mStringMiddlePoint.y, this.mPaint);
        canvas.drawLine(this.mStringEndPoint.x, this.mStringEndPoint.y, this.mStringMiddlePoint.x, this.mStringMiddlePoint.y, this.mPaint);
    }

    private void drawArrow(Canvas canvas) {
        this.mPaint.setStyle(Paint.Style.FILL_STYLE);
        this.mPaint.setColor(new Color(this.mArrowColor));
        canvas.drawPath(this.mArrowPath, this.mPaint);
    }

    private void drawArrowTail(Canvas canvas) {
        this.drawArrowTail(canvas, 255);
    }

    private void drawArrowTail(Canvas canvas, int alpha) {
        this.mPaint.setStyle(Paint.Style.FILL_STYLE);
        this.mPaint.setColor(new Color(this.mArrowColor));
        this.mPaint.setAlpha(toFloat(alpha));
        this.mPaint.setMaskFilter(this.mTailMaskFilter);

        RectFloat rectFloat = new RectFloat();
        rectFloat.left = this.mArrowTail.left;
        rectFloat.top = this.mArrowTail.top;
        rectFloat.right = this.mArrowTail.right;
        rectFloat.bottom = this.mArrowTail.bottom;
        canvas.drawRect(rectFloat, this.mPaint);
        this.mPaint.setMaskFilter((MaskFilter)null);
    }

    private void updateArrowOffset() {
        float newOffset = 0.0F;
        if (this.mProgress > 0.5F) {
            newOffset = this.mStringOffset;
        } else if (this.mProgress >= 0.25F) {
            newOffset = (this.mProgress - 0.25F) * this.mStringOffset * 4.0F;
        }

        this.mArrowPath.offset(0.0F, -this.mArrowOffset);
        this.mArrowPath.offset(0.0F, this.mArrowOffset = newOffset);
    }

    private void updateHandlePath() {
        float bowPathLength = this.mBowPathMeasure.getLength();
        float handlePathLength = bowPathLength / 5.0F;
        float center = bowPathLength / 2.0F;
        float start = center - handlePathLength / 2.0F;
        this.mHandlePath.reset();
        this.mBowPathMeasure.getSegment(start, start + handlePathLength, this.mHandlePath, true);
    }

    private void updateLinesY() {
        Iterator var1 = this.mLines.iterator();

        while(var1.hasNext()) {
            ArrowDrawable.Line tmp = (ArrowDrawable.Line)var1.next();
            float runtime = (float)(Time.getRealActiveTime() - tmp.startTime);
            float percent = runtime / (float)tmp.duration;
            tmp.startY = percent * tmp.distance - tmp.height;
            if (tmp.startY >= (float)this.mHeight) {
                this.initLines(tmp);
            }
        }

    }

    private void updateStringPoints() {
        this.updateStringPoints(true);
    }

    private void updateStringPoints(boolean updateMiddlePointY) {
        int length = this.mBowPathPoints.length;
        int stringStartIndex = (int)((float)length * 0.05F);
        if (stringStartIndex % 2 != 0) {
            --stringStartIndex;
        }

        int stringEndIndex = (int)((float)length * 0.95F);
        if (stringEndIndex % 2 != 0) {
            --stringEndIndex;
        }

        this.mStringStartPoint.x = this.mBowPathPoints[stringStartIndex];
        this.mStringStartPoint.y = this.mBowPathPoints[stringStartIndex + 1]; 
        this.mStringEndPoint.x = this.mBowPathPoints[stringEndIndex];
        this.mStringEndPoint.y = this.mBowPathPoints[stringEndIndex + 1];
        if (updateMiddlePointY) {
            this.mStringMiddlePoint.y = this.mStringOffset = this.mStringStartPoint.y + (this.mProgress <= 0.5F ? 0.0F : (this.mProgress - 0.5F) * this.mMaxStringOffset * 2.0F);
        }

    }

    private void updateBowPath(float currentAngle) {
        PointF stringPoint = this.getPointByAngle(currentAngle);
        float startX = this.mCenterX * 2.0F - stringPoint.x;
        float startY = stringPoint.y;
        float controlX = this.mCenterX;
        float controlY = -stringPoint.y;
        float endX = stringPoint.x;
        float endY = stringPoint.y;
        this.mBowPath.reset();
        this.mBowPath.moveTo(startX, startY);
        this.mBowPath.quadTo(controlX, controlY, endX, endY);
        float offsetY = -this.mBaseStringCenterY;
        offsetY += this.mMaxBowOffset * (this.mProgress <= 0.25F ? this.mProgress * 4.0F : 1.0F);
        this.mBowPath.offset(0.0F, offsetY);
    }

    private void initArrowPath(float arrowBodyLength) {
        this.mArrowPath.reset();
        this.mArrowPath.moveTo(this.mCenterX + this.mArrowBodyWidth, -this.mFinSlopeHeight);
        this.mArrowPath.rLineTo(this.mFinWidth, this.mFinSlopeHeight);
        this.mArrowPath.rLineTo(0.0F, -this.mFinHeight);
        this.mArrowPath.rLineTo(-this.mFinWidth, -this.mFinSlopeHeight);
        this.mArrowPath.rLineTo(0.0F, -arrowBodyLength);
        this.mArrowPath.rLineTo(this.mArrowWidth, 0.0F);
        this.mArrowPath.rLineTo(-this.mArrowWidth - this.mArrowBodyWidth, -this.mArrowHeight);
        this.mArrowPath.rLineTo(-this.mArrowWidth - this.mArrowBodyWidth, this.mArrowHeight);
        this.mArrowPath.rLineTo(this.mArrowWidth, 0.0F);
        this.mArrowPath.rLineTo(0.0F, arrowBodyLength);
        this.mArrowPath.rLineTo(-this.mFinWidth, this.mFinSlopeHeight);
        this.mArrowPath.rLineTo(0.0F, this.mFinHeight);
        this.mArrowPath.rLineTo(this.mFinWidth, -this.mFinSlopeHeight);
        this.mArrowPath.close();
    }

    private float getAngleByProgress() {
        return 25.0F + (this.mProgress <= 0.5F ? 0.0F : 20.0F * (this.mProgress - 0.5F) * 2.0F);
    }

    private PointF getPointByAngle(float angle) {
        double radian = (double)angle * 3.141592653589793D / 180.0D;
        float radius = this.mBowLength / 2.0F;
        float xP = (float)((double)this.mCenterX + (double)radius * Math.cos(radian));
        float yP = (float)((double)radius * Math.sin(radian));
        this.mTempPoint.set(xP, yP);
        return this.mTempPoint;
    }

    private float[] decomposePath(PathMeasure pathMeasure) {
        if (pathMeasure.getLength() == 0.0F) {
            return new float[0];
        } else {
            float pathLength = pathMeasure.getLength();
            int numPoints = (int)(pathLength / this.mPrecision) + 1;
            float[] points = new float[numPoints * 2];
            //float[] position = new float[2];
            Matrix matrix = new Matrix();
            int index = 0;
            for(int i = 0; i < numPoints; ++i) {
                float distance = (float)i * pathLength / (float)(numPoints - 1);
                //pathMeasure.getPosTan(distance, position, (float[])null);
                pathMeasure.getMatrix(distance, matrix, PathMeasure.GET_POSITION_MATRIX_FLAG);
                float[] data = matrix.getData();
                points[index] = data[2];
                points[index + 1] = data[5];
                index += 2;
            }

            return points;
        }
    }

    /***
     * 设置精度
     * @param precision 精度
     */
    public void setPrecision(float precision) {
        this.mPrecision = precision;
        this.invalidate();
    }

    /***
     * 获得固有宽度
     * @return 返回宽度
     */
    public int getIntrinsicWidth() {
        return this.mWidth;
    }

    /***
     * 获取内在高度
     * @return 返回高度
     */
    public int getIntrinsicHeight() {
        return this.mHeight;
    }

    /***
     * 设置透明度
     * @param alpha 透明度
     */
    public void setAlpha(int alpha) {
        this.mPaint.setAlpha(toFloat(alpha));
    }

    /***
     * 设置颜色过滤器
     * @param colorFilter 颜色过滤器
     */
    public void setColorFilter(ColorFilter colorFilter) {
        this.mPaint.setColorFilter(colorFilter);
    }

    /**
     * 获取不透明度
     * @return 不透明度
     */
    public int getOpacity() {
        return -3;
    }

    public float getProgress() {
        return this.mProgress;
    }

    /**
     * 设置进度
     * @param progress Range(from = 0.0D,to = 1.0D)
     */
    public void setProgress( float progress) {
        if (this.mState == 0 || this.mState == 1) {
            this.mState = 1;
            if (progress > 1.0F) {
                progress = 1.0F;
            } else if (progress < 0.0F) {
                progress = 0.0F;
            }

            this.mProgress = progress;
            this.invalidate();
        }

    }

    /**
     * 获取基线下降持续时间
     * @return  基线下降持续时间
     */
    public int getBaseLinesFallDuration() {
        return this.mBaseLinesFallDuration;
    }

    /**
     * 设置基线下降持续时间
     * @param duration 时间
     */
    public void setBaseLinesFallDuration(int duration) {
        this.mBaseLinesFallDuration = duration;
    }

    /***
     * 获得射击弓坠落持续时间
     * @return 射击弓坠落持续时间
     */
    public long getFiringBowFallDuration() {
        return this.mFiringBowFallDuration;
    }

    /***
     * 设置射击弓坠落持续时间
     * @param duration 时间
     */
    public void setFiringBowFallDuration(long duration) {
        this.mFiringBowFallDuration = duration;
    }

    /**
     * 获得发射箭缩短持续时间
     * @return 发射箭缩短持续时间
     */
    public long getFiredArrowShrinkDuration() {
        return this.mFiredArrowShrinkDuration;
    }

    /**
     * 设置发射箭收缩持续时间
     * @param duration 时间
     */
    public void setFiredArrowShrinkDuration(long duration) {
        this.mFiredArrowShrinkDuration = duration;
    }

    /**
     * 获得发射箭移动持续时间
     * @return 发射箭移动持续时间
     */
    public long getFiredArrowMoveDuration() {
        return this.mFiredArrowMoveDuration;
    }

    /***
     * 设置发射箭头移动持续时间
     * @param duration 时间
     */
    public void setFiredArrowMoveDuration(long duration) {
        this.mFiredArrowMoveDuration = duration;
    }

    /**
     * 获得丢失持续时间
     * @return 丢失持续时间
     */
    public long getMissDuration() {
        return this.mMissDuration;
    }

    /**
     * 设置丢失时长
     * @param duration 时间
     */
    public void setMissDuration(long duration) {
        this.mMissDuration = duration;
    }

    /***
     * 获得击中持续时间
     * @return 击中持续时间
     */
    public long getHitDuration() {
        return this.mHitDuration;
    }

    /**
     * 设置击中持续时间
     * @param duration 时间
     */
    public void setHitDuration(long duration) {
        this.mHitDuration = duration;
    }

    /**
     * 获取倾斜持续时间
     * @return 倾斜持续时间
     */
    public float getSkewDuration() {
        return this.mSkewDuration;
    }

    /**
     * 设置倾斜持续时间
     * @param duration 时间
     */
    public void setSkewDuration(float duration) {
        this.mSkewDuration = duration;
    }

    /**
     * 获取线条颜色
     * @return 线条颜色
     */
    public int getLineColor() {
        return this.mLineColor;
    }

    /**
     * 设置线条颜色
     * @param color 颜色
     */
    public void setLineColor(int color) {
        this.mLineColor = color;
        this.invalidate();
    }

    /***
     * 获得弓颜色
     * @return 弓颜色
     */
    public int getBowColor() {
        return this.mBowColor;
    }

    /**
     * 设置弓颜色
     * @param color 颜色
     */
    public void setBowColor(int color) {
        this.mBowColor = color;
        this.invalidate();
    }

    /**
     * 获取弦颜色
     * @return 弦颜色
     */
    public int getStringColor() {
        return this.mStringColor;
    }

    /**
     * 设置弦颜色
     * @param color 弦颜色
     */
    public void setStringColor(int color) {
        this.mStringColor = color;
        this.invalidate();
    }

    /**
     * 获取箭头颜色
     * @return 箭头颜色
     */
    public int getArrowColor() {
        return this.mArrowColor;
    }

    /***
     * 设置箭头颜色
     * @param color 箭头颜色
     */
    public void setArrowColor(int color) {
        this.mArrowColor = color;
        this.invalidate();
    }

    /**
     * 得到歪斜棕褐色
     * @return 歪斜棕褐色
     */
    public float getSkewTan() {
        return this.mSkewTan;
    }

    /**
     * 设置歪斜棕褐色
     * @param tan 歪斜棕褐色
     */
    public void setSkewTan(float tan) {
        this.mSkewTan = tan;
    }

    /**
     * 获得最大倾斜计数
     * @return 最大倾斜计数
     */
    public int getMaxSkewCount() {
        return this.mMaxSkewCount;
    }

    /**
     * 设置最大倾斜计数
     * @param count 最大倾斜计数
     */
    public void setMaxSkewCount(int count) {
        this.mMaxSkewCount = count;
    }

    private static class Line {
        long duration;
        long startTime;
        float distance;
        float startX;
        float startY;
        float height;
        float endX;

        private Line() {
        }
    }
}

